﻿#include "platform.h"
#include "string_utils.h"
#include <cstdarg>
#include <vector>

static const int MAX_VA_BUF_SIZE = 8 * 1024 * 1024;

void stringAppendV(std::string& buf, const char* fmt, va_list args)
{
    const int STACK_BUF_SIZE = 1024;
    char stack_buf[STACK_BUF_SIZE];

    va_list ap_copy;
    va_copy(ap_copy, args);

    int result = std::vsnprintf(stack_buf, STACK_BUF_SIZE, fmt, ap_copy);
    va_end(ap_copy);

    if (result >= 0 && result < STACK_BUF_SIZE) {
        buf.append(stack_buf, result);
        return;
    }

    // 重复增大缓冲区的大小，直到它合适
    int mem_length = STACK_BUF_SIZE;
    while (true) {
        if (result < 0) {
#if defined(PLATFORM_WINDOWS)
#else
            if (errno != 0 && errno != EOVERFLOW)
                return;
            // 缓冲区大小增大一倍
            mem_length *= 2;
#endif
        } else {
            mem_length = result + 1;
        }

        if (mem_length > MAX_VA_BUF_SIZE) {
            return;
        }

        std::vector<char> mem_buf(mem_length);
        va_copy(ap_copy, args);
        result = std::vsnprintf(&mem_buf[0], mem_length, fmt, ap_copy);
        va_end(ap_copy);

        if ((result >= 0) && (result < mem_length)) {
            buf.append(&mem_buf[0], result);
            return;
        }
    }
}


void stringAppendF(std::string& buf, const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    stringAppendV(buf, fmt, args);
    va_end(args);
}


std::unique_ptr<std::string> stringFormatV(const char* fmt, va_list args)
{
    std::unique_ptr<std::string> pstr(new std::string());
    stringAppendV(*pstr, fmt, args);
    return pstr;
}


std::unique_ptr<std::string> stringFormat(const char* fmt, ...)
{
    va_list args;
    std::unique_ptr<std::string> pstr(new std::string());
    va_start(args, fmt);
    stringAppendV(*pstr, fmt, args);
    va_end(args);
    return pstr;
}


std::unique_ptr<std::string> bytesToHexString(const uint8_t* data, int count)
{
    const char* HEX_DIGITS = "0123456789ABCDEF";
    std::unique_ptr<std::string> pstr(new std::string());

    if (data != nullptr && count > 0) {
        pstr->resize(count * 2 + 1);

        int i = 0, j = 0;
        for (; i < count; i++) {
            (*pstr)[j++] = HEX_DIGITS[data[i] >> 4];
            (*pstr)[j++] = HEX_DIGITS[data[i] & 0xf];
        }
        (*pstr)[j] = 0;
    }

    return pstr;
}


std::unique_ptr<std::string> bytesToPrintableString(const uint8_t* data, int count)
{
    std::unique_ptr<std::string> pstr(new std::string());

    if (data != nullptr && count > 0) {
        pstr->resize(count * 2 + 1);
        for (int i = 0; i < count; i++) {
            if (isprint(data[i]))
                (*pstr)[i] = data[i];
            else
                (*pstr)[i] = '.';
        }
        (*pstr)[count] = 0;
    }

    return pstr;
}


std::unique_ptr<std::string> formatSystemError(long long err_code)
{
    std::unique_ptr<std::string> pstr(new std::string());

#if defined(PLATFORM_WINDOWS)
    const DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
    const DWORD lang_id = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);

    LPSTR buf = nullptr;
    ::FormatMessageA(flags, NULL, (DWORD)err_code, lang_id, (LPSTR)&buf, 0, NULL);
    if (buf != nullptr) {
        *pstr = buf;
        ::LocalFree(buf);
    }

    return pstr;
#else
    *pstr = ::strerror((int)err_code);
    return pstr;
#endif
}
